﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Assimp;
using Tao.OpenGl;
using System.Drawing;
using System.Drawing.Imaging;

namespace RacunarskaGrafikaProjekat.source {
    class CWorld:IDisposable {
        protected Glu.GLUquadric quadricObj;
        protected CBitmapText textObj;

        #region Property
        protected String ModelFolderPath {
            get;
            set;
        }

        protected List<String> TexturePath {
            get;
            set;
        }

        protected int[] TextureIndex {
            get;
            set;
        }

        protected List<String> ModelFileName {
            get;
            set;
        }

        protected List<CAssimpScene> Scene {
            get;
            set;
        }

        public float SceneRotationX {
            get;
            set;
        }

        public float SceneRotationY {
            get;
            set;
        }

        public float SceneDistance {
            get;
            set;
        }

        public int ControlHeight {
            get;
            set;
        }

        public int ControlWidth {
            get;
            set;
        }

        public int runwayBulbs {
            get;
            set;
        }

        public int runwayBulbsIndex {
            get;
            set;
        }

        public float runwayLength {
            get;
            set;
        }

        public float runwayHeight {
            get;
            set;
        }

        public float[] runwayBulbsColor {
            get;
            set;
        }

        public float[] airplanePosition {
            get;
            set;
        }

        public float[] airplaneRotation {
            get;
            set;
        }

        public float[] spotDirection {
            get;
            set;
        }

        public float[] spotPosition {
            get;
            set;
        }

        public float[] cameraEye {
            get;
            set;
        }

        public float[] cameraDirection {
            get;
            set;
        }

        public float[] cameraUp {
            get;
            set;
        }

        #endregion Property

        public CWorld(String folderPath,List<String> filePath,List<String> texturePath,int cHeight,int cWidth) {
            ControlHeight=cHeight;
            ControlWidth=cWidth;

            ModelFolderPath=folderPath;
            ModelFileName=filePath;
            TexturePath=texturePath;

            quadricObj=Glu.gluNewQuadric();
            textObj=new CBitmapText("Tahoma", 14, false, true, true, false);

            initialize();
            resize();
        }

        ~CWorld(){
            this.Dispose(false);
        }

        protected void initialize() {
            //set quadricObj properties
            Glu.gluQuadricNormals(quadricObj, Glu.GLU_SMOOTH);

            //load airplane
            Scene=new List<CAssimpScene>();
            foreach(String f in ModelFileName) {
                Scene.Add(new CAssimpScene(ModelFolderPath, f));
            }

            //initialize properties
            SceneRotationX=0.0f;
            SceneRotationY=0.0f;
            SceneDistance=0.0f;
            runwayBulbs=7;
            runwayLength=2000.0f;
            runwayHeight=10.0f;
            runwayBulbsIndex=0;

            runwayBulbsColor=new float[3]{1.0f,0.0f,0.0f};

            initAirplanePosition();

            Gl.glEnable(Gl.GL_DEPTH_TEST);
            Gl.glEnable(Gl.GL_CULL_FACE);

            float[] ambientMat = { 0.2f, 0.2f, 0.2f, 1.0f };
            float[] specularMat = { 0f, 0f, 0f, 1.0f };
            float[] diffuseMat = { 1f, 1f, 1f, 1.0f };
            Gl.glEnable(Gl.GL_COLOR_MATERIAL);
            Gl.glColorMaterial(Gl.GL_FRONT, Gl.GL_AMBIENT_AND_DIFFUSE);
            Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_SPECULAR, specularMat);
            Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_DIFFUSE, ambientMat);
            Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_DIFFUSE, diffuseMat);
            Gl.glMateriali(Gl.GL_FRONT, Gl.GL_SHININESS, 128);

            spotDirection = new float[3] { 0.0f, 0.0f, runwayLength / 2 };
            spotPosition = new float[4] { 0.0f, 100.0f, -runwayLength / 2, 1.0f };
            float globalLightFactor=20;
            float[] ambientLight0= { 0.2f*globalLightFactor, 0.2f*globalLightFactor, 0.2f*globalLightFactor, 1f };
            float[] diffuseLight0= { 1f*globalLightFactor, 1f*globalLightFactor, 1f*globalLightFactor, 1f };
            float[] specularLight0= { 1f*globalLightFactor, 1f*globalLightFactor, 1f*globalLightFactor, 1f };
            // Ukljuci proracun osvetljenja
            Gl.glEnable(Gl.GL_LIGHTING);
            //Gl.glLightModelfv(Gl.GL_LIGHT_MODEL_AMBIENT, ambientLight0);
            Gl.glShadeModel(Gl.GL_SMOOTH);

            // Svetlosni izvor 0 podesavanja - glavni svetlosni izvor
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, ambientLight0);
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, diffuseLight0);
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_SPECULAR, specularLight0);
            Gl.glLightf(Gl.GL_LIGHT0, Gl.GL_SPOT_CUTOFF, 30.0f);
            Gl.glLightf(Gl.GL_LIGHT0, Gl.GL_SPOT_EXPONENT, 0.0f);
            Gl.glEnable(Gl.GL_LIGHT0);

            //izvor za svetiljke na pisti
            float lightInt=4f;
            float[] ambientLight1= { 0f, 0, 0f, 1f };
            float[] diffuseLight1= { lightInt, lightInt, lightInt, 1f };
            float[] specularLight1= { lightInt, lightInt, lightInt, 1f };
            Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_AMBIENT, ambientLight1);
            Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_DIFFUSE, diffuseLight1);
            Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_SPECULAR, specularLight1);
            Gl.glLightf(Gl.GL_LIGHT1, Gl.GL_SPOT_CUTOFF, 2.0f);
            Gl.glLightf(Gl.GL_LIGHT1, Gl.GL_SPOT_EXPONENT, 0.0f);

            //Svetlosni izvor 4 - zeleno svetlo iznad aviona
            float greenInt=4f;
            float[] ambientLight4= { 0f, 0, 0f, 1f };
            float[] diffuseLight4= { 0f, greenInt, 0f, 1f };
            float[] specularLight4= { 0f, greenInt, 0f, 1f };
            Gl.glLightfv(Gl.GL_LIGHT4, Gl.GL_AMBIENT, ambientLight4);
            Gl.glLightfv(Gl.GL_LIGHT4, Gl.GL_DIFFUSE, diffuseLight4);
            Gl.glLightfv(Gl.GL_LIGHT4, Gl.GL_SPECULAR, specularLight4);
            Gl.glLightf(Gl.GL_LIGHT4, Gl.GL_SPOT_CUTOFF, 2.0f);
            Gl.glLightf(Gl.GL_LIGHT4, Gl.GL_SPOT_EXPONENT, 0.0f);
            //Gl.glEnable(Gl.GL_LIGHT4);

            //inicijalizacija pogleda
            cameraEye=new float[3] {runwayLength/8,40,0};
            cameraDirection=new float[3] { 0, 0, runwayLength/2 };
            cameraUp=new float[3] {0,1,0 };

            textureInit();

            // Boja pozadine je bela
            Gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        }

        protected void textureInit() {
            // Ukljuci automatsku normalizaciju nad normalama
            Gl.glEnable(Gl.GL_NORMALIZE);            
            // Ukljuciti rezim rada sa 2D teksturama i podesiti nacin stapanja na Gl.GL_DECAL
            Gl.glEnable(Gl.GL_TEXTURE_2D);
            // Podesi nacin blending teksture
            Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_TEXTURE_ENV_MODE, Gl.GL_MODULATE);

            TextureIndex=new int[TexturePath.Count];

            // Ucitaj slike i kreiraj teksture
            Gl.glGenTextures(2, TextureIndex);
            
            // Slika teksture
            Bitmap image;

            for(int i=0;i<TexturePath.Count;i++) {
                // Pridruzi teksturu odgovarajucem identifikatoru
                Gl.glBindTexture(Gl.GL_TEXTURE_2D, TextureIndex[i]);

                // Ucitaj sliku i podesi parametre teksture
                image=new Bitmap(ModelFolderPath+"\\"+TexturePath[i]);

                // rotiramo sliku zbog koordinantog sistema opengl-a
                image.RotateFlip(RotateFlipType.RotateNoneFlipY);
                Rectangle rect=new Rectangle(0, 0, image.Width, image.Height);
                // RGBA format (dozvoljena providnost slike tj. alfa kanal)
                BitmapData imageData=image.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly,
                                                      System.Drawing.Imaging.PixelFormat.Format32bppArgb);

                Glu.gluBuild2DMipmaps(Gl.GL_TEXTURE_2D, (int)Gl.GL_RGBA8, image.Width, image.Height, Gl.GL_BGRA, Gl.GL_UNSIGNED_BYTE, imageData.Scan0);

                // Podesiti texture filtre: max = linear, min=linear_mipmap_linear,
                Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR_MIPMAP_LINEAR);
                Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR_MIPMAP_LINEAR);

                Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_REPEAT);
                Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_REPEAT);

                image.UnlockBits(imageData);
                image.Dispose();
            }
        }

        public void initAirplanePosition() {
            airplanePosition=new float[3] { 0.0f, runwayHeight+20, runwayLength/2-90 };
            airplaneRotation=new float[3] { 0.0f, 0.0f, 0.0f };
        }

        public void draw() {
            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT|Gl.GL_DEPTH_BUFFER_BIT);
 

            Gl.glPushMatrix();

            //postavljanje pogleda
            Glu.gluLookAt(cameraEye[0], cameraEye[1], cameraEye[2],
                cameraDirection[0], cameraDirection[1], cameraDirection[2],
                cameraUp[0], cameraUp[1], cameraUp[2]);

            drawText();
            
            Gl.glTranslatef(0.0f, 0.0f, -SceneDistance);
            Gl.glRotatef(SceneRotationX, 1.0f, 0.0f, 0.0f);
            Gl.glRotatef(SceneRotationY, 0.0f, 1.0f, 0.0f);


            //postaviti globalno osvetljenje
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, spotPosition);
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_SPOT_DIRECTION, spotDirection);           

            drawAirport();
            drawRunway();
            drawAirplane();

            Gl.glPopMatrix();
            // Oznaci kraj iscrtavanja
            Gl.glFlush();
        }
        
        protected void drawAirport() {
            Gl.glColor3f(0.0f, 1.0f, 0.0f);
            float d=2000.0f;
            float e=0.0f;
            Gl.glEnable(Gl.GL_TEXTURE_2D);
            Gl.glBindTexture(Gl.GL_TEXTURE_2D, TextureIndex[1]);

            float textMulti = 10;
            Gl.glBegin(Gl.GL_QUADS);
                Gl.glNormal3f(0, 0, 1);
                Gl.glTexCoord2f(1.0f * textMulti, 1.0f * textMulti);
                Gl.glVertex3f(-d, e, d);
                Gl.glTexCoord2f(0.0f * textMulti, 1.0f * textMulti);
                Gl.glVertex3f(d, e, d);
                Gl.glTexCoord2f(1.0f * textMulti, 0.0f * textMulti);
                Gl.glVertex3f(d, e, -d);
                Gl.glTexCoord2f(0.0f * textMulti, 0.0f * textMulti);
                Gl.glVertex3f(-d, e, -d);
            Gl.glEnd();
            Gl.glDisable(Gl.GL_TEXTURE_2D);
            
        }

        protected void drawRunway() {
            Gl.glPushMatrix();

            //boja piste
            float gray=(1.0f/255.0f)*40.0f;
            Gl.glColor3f(gray, gray, gray);

            Gl.glTranslatef(0, 5, 0);
            float factor=10;
            CBox box=new CBox(runwayLength/factor, runwayHeight, runwayLength,TextureIndex[0]);
            box.Draw();
            
            float distance=(runwayLength)/(runwayBulbs+1);
            float widthFactor=((runwayLength/factor)-20)/2;
            float bulbRadius=2.0f;

            Gl.glTranslatef(widthFactor, runwayHeight/2, (runwayLength/2)-distance);
            float[] lightPosition= { 0, 3, 0 };
            float[] lightDirection= { 0, -1, 0, 1 };
            Gl.glColor3f(runwayBulbsColor[0], runwayBulbsColor[1], runwayBulbsColor[2]);
            Gl.glDisable(Gl.GL_LIGHT0);
            for(int i=0;i<runwayBulbs;i++) {
                if(i==runwayBulbsIndex) { 
                    Gl.glEnable(Gl.GL_LIGHT1);
                    Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_POSITION, lightPosition);
                    Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_SPOT_DIRECTION, lightDirection);
                    Glu.gluSphere(quadricObj, bulbRadius, 128, 128);
                    Gl.glDisable(Gl.GL_LIGHT1);
                } else {
                    Glu.gluSphere(quadricObj, bulbRadius, 128, 128);
                }

                Gl.glTranslatef(-2*widthFactor, 0, 0);

                if(i==runwayBulbsIndex) {
                    Gl.glEnable(Gl.GL_LIGHT1);
                    Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_POSITION, lightPosition);
                    Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_SPOT_DIRECTION, lightDirection);
                    Glu.gluSphere(quadricObj, bulbRadius, 128, 128);
                    Gl.glDisable(Gl.GL_LIGHT1);
                    
                }else{
                    Glu.gluSphere(quadricObj, bulbRadius, 128, 128);
                }
                Gl.glTranslatef(2*widthFactor, 0, -distance);
            }
            Gl.glEnable(Gl.GL_LIGHT0);
            Gl.glPopMatrix();
        }

        protected void drawAirplane() {
            Gl.glPushMatrix();
            //translirati avion kako je potrebno - zbog animacije
            Gl.glTranslatef(airplanePosition[0], airplanePosition[1], airplanePosition[2]);
            Gl.glRotatef(airplaneRotation[0], 1, 0, 0);
            Gl.glRotatef(airplaneRotation[1], 0, 1, 0);
            Gl.glRotatef(airplaneRotation[2], 0, 0, 1);

            foreach(CAssimpScene scene in Scene) {
                scene.Draw();    
            }

            //zeleno svetlo na vrhu
            float airplaneHeight=10;
            float[] greenPosition= { 0, 3, 0 };
            float[] greenDirection= { 0, -1, 0, 1 };
            Gl.glTranslatef(0, airplaneHeight, -10);
            Gl.glDisable(Gl.GL_LIGHT0);
            Gl.glEnable(Gl.GL_LIGHT4);
            Gl.glLightfv(Gl.GL_LIGHT4, Gl.GL_POSITION, greenPosition);
            Gl.glLightfv(Gl.GL_LIGHT4, Gl.GL_SPOT_DIRECTION, greenDirection);
            Gl.glColor3f(1, 1, 1);
            Glu.gluSphere(quadricObj, 1, 128, 128);
            Gl.glDisable(Gl.GL_LIGHT4);
            Gl.glEnable(Gl.GL_LIGHT0);


            Gl.glPopMatrix();
        }

        protected void drawText() {
            String text1="Predmet: Racunarska grafika";
            String text2="Sk.god: 2013./14";
            String text3="Ime: Vojislav";
            String text4="Prezime: Djukic";
            String text5="Sifra zad: 6.2";
            Gl.glPushMatrix();

            Gl.glDisable(Gl.GL_LIGHTING);
            Gl.glDisable(Gl.GL_COLOR_MATERIAL);

            Gl.glMatrixMode(Gl.GL_PROJECTION);      // selektuj Projection Matrix
            Gl.glPushMatrix();
            Gl.glLoadIdentity();			        // resetuj Projection Matrix
            Glu.gluOrtho2D(-ControlWidth, ControlWidth, -ControlHeight, ControlHeight);
            Gl.glMatrixMode(Gl.GL_MODELVIEW);   // selektuj ModelView Matrix
            Gl.glLoadIdentity();                // resetuj ModelView Matrix

            //boja teksta
            Gl.glColor3f(0, 0, 1);
            int step=60;
            Gl.glRasterPos2d(-ControlWidth+10, ControlHeight-50);
            textObj.DrawText(text1);
            
            Gl.glRasterPos2d(-ControlWidth+10, ControlHeight-50-1*step);
            textObj.DrawText(text2);

            Gl.glRasterPos2d(-ControlWidth+10, ControlHeight-50-2*step);
            textObj.DrawText(text3);

            Gl.glRasterPos2d(-ControlWidth+10, ControlHeight-50-3*step);
            textObj.DrawText(text4);

            Gl.glRasterPos2d(-ControlWidth+10, ControlHeight-50-4*step);
            textObj.DrawText(text5);

            Gl.glMatrixMode(Gl.GL_PROJECTION);      // selektuj Projection Matrix
            Gl.glLoadIdentity();			        // resetuj Projection Matrix
            Gl.glPopMatrix();
            Gl.glMatrixMode(Gl.GL_MODELVIEW);   // selektuj ModelView Matrix

            Gl.glEnable(Gl.GL_COLOR_MATERIAL);
            Gl.glEnable(Gl.GL_LIGHTING);

            Gl.glPopMatrix();
        }

        public void resize() {
            Gl.glViewport(0, 0, ControlWidth, ControlHeight); // kreiraj viewport po celom prozoru
            Gl.glMatrixMode(Gl.GL_PROJECTION);      // selektuj Projection Matrix
            Gl.glLoadIdentity();			        // resetuj Projection Matrix

            Glu.gluPerspective(60.0, (double)ControlWidth/(double)ControlHeight, 1.5, 20000.0);

            Gl.glMatrixMode(Gl.GL_MODELVIEW);   // selektuj ModelView Matrix
            Gl.glLoadIdentity();                // resetuj ModelView Matrix
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Oslodi managed resurse
            }

            Glu.gluDeleteQuadric(quadricObj);
            textObj.Dispose();
            // Oslobodi unmanaged resurse
            foreach(CAssimpScene scene in Scene){
                scene.Dispose();
            }
        }

        #region IDisposable metode

        /// <summary>
        ///  Dispose metoda.
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion IDisposable metode
    }
}
